/*****************************************************************************/
/*                                                                           */
/*    Domino Operation System System Specific Module                         */
/*                                                                           */
/*    Copyright (C) 2010 Laszlo Arvai                                        */
/*                                                                           */
/*    ------------------------------------------------------------------     */
/*    netSlip - Network SLIP implementation                                  */
/*****************************************************************************/

//! \defgroup ethernet Ethernet SLIP

/*****************************************************************************/
/* Includes                                                                  */
/*****************************************************************************/
#include <netSLIP.h>
#include <drvUART.h>
#include <netPacketBuffer.h>
#include <sysCompiler.h>
#include "drvIOConfig.h"

/*****************************************************************************/
/* Constants                                                                 */
/*****************************************************************************/

// status codes
#define netSRXS_WAITING_FOR_PACKET 1
#define netSRXS_RECEIVING_PACKET   2
#define netSRXS_ESC_RECEIVED       3

// special bytes
#define netSLIP_END  0xC0  //300 octal
#define netSLIP_ESC  0xDB  //333 octal
#define netESC_END   0xDC  //334 octal
#define netESC_ESC   0xDD  //335 octal

// other constants
#define netMODEM_COMMAND_BUFFER_LENGTH 16
#define netSLIP_MAX_SIZE 1006

/*****************************************************************************/
/* Module global variables                                                   */
/*****************************************************************************/ 
static dosByte l_slip_rx_status = netSRXS_WAITING_FOR_PACKET;  // status byte of the SLIP RX module

// SLIP internal RX variables
static dosWord l_rx_frame_size;

// SLIP TX variables
static dosByte l_tx_pending_char;
static dosByte l_tx_pending_char_valid;

// modem command buffer
static dosChar l_modem_command_buffer[netMODEM_COMMAND_BUFFER_LENGTH];
static dosByte l_modem_rx_pointer	= 0;
static dosWord l_modem_tx_pointer = 0;
static dosWord l_modem_tx_length	= 0;

// connection command string
static dosConstString l_connect_request = "CLIENT";
static dosConstString l_connect_response = "CLIENTSERVER";

/*****************************************************************************/
/* Functions                                                                 */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
//! Inicializes SLIP
//! This function must be called before using any SLIP function.
void netMACInitialize(void)
{   
  // init
  l_slip_rx_status = netSRXS_WAITING_FOR_PACKET;

  // clear buffers
  l_modem_tx_length = 0;                   // clear TX buffer
  l_modem_tx_length = 0;
  l_modem_rx_pointer= 0;             // clear modem command buffer
  l_modem_command_buffer[0] = '\0';

  // init other modules
  drvSLIPUARTInit();
}

///////////////////////////////////////////////////////////////////////////////
//! Starts packet transmission
void netMACStartTransmission(void)
{
	drvSLIPUARTEnableTransmitInterrupt();
}

/*****************************************************************************/
/* Internal functions                                                        */
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// UART Transmit complete interrupt
void drvSLIPUARTTransmitInterrupt(void)
{
	dosByte curr_byte;
	
	// check if there are pending characters in the modem buffer
	if( l_modem_tx_length > 0 )
	{
		// check if data is still pending
		if( l_modem_tx_pointer < l_modem_tx_length )
		{
			// send character
			drvSLIPUARTSendCharacter( l_modem_command_buffer[l_modem_tx_pointer++] );
		}
		else
		{
			// clear length and disable interrupt if there is no more character to send
			l_modem_tx_length = 0;
			drvSLIPUARTDisableTransmitInterrupt();
		}
	}
	else
	{
		// check if character pending
		if( l_tx_pending_char_valid )
		{
			// send character and invalidate pending flag
			drvSLIPUARTSendCharacter( l_tx_pending_char );
			l_tx_pending_char_valid = dosFalse;
		}
		else
		{
			// check if packjet is pending
			if( netIsTXPacketAvailable() )
			{
				// check if TX packet start
				if( netIsTXPacketStart() )
				{
					// packet start flag
					drvSLIPUARTSendCharacter( netSLIP_END );
					
					// store first character and clearing packet start flag
					if( netReadByteTXPacket( &l_tx_pending_char ) )
						l_tx_pending_char_valid = dosTrue;
				}
				else
				{
					// send packet content
					if( netReadByteTXPacket( &curr_byte ) )
					{
						// check to see if is a special character
						switch(curr_byte)
						{
							// escape special character
							case netSLIP_END:
								curr_byte = netSLIP_ESC;
								l_tx_pending_char = netESC_END;
								l_tx_pending_char_valid = dosTrue;
								break;

							// escape special character
							case netSLIP_ESC:
								curr_byte = netSLIP_ESC;
								l_tx_pending_char = netESC_ESC;
								l_tx_pending_char_valid = dosTrue;
								break;

							// send raw character
							default:
								break;
						}
						
						// send byte
						drvSLIPUARTSendCharacter( curr_byte );
					}
					else
					{
						// end of packet
						drvSLIPUARTSendCharacter( netSLIP_END );					
					}
				}
			}
			else
			{
				// no more characters to send
				drvSLIPUARTDisableTransmitInterrupt();				
			}
		}		
	}
}

///////////////////////////////////////////////////////////////////////////////
//! Character received interrupt callback
//! Usually this function is called from the driver RX interrupt handler.
//! \param Received character
void drvSLIPUARTReceiveInterrupt( dosChar in_char )
{
  dosByte request_length;
  dosByte i;
  dosByte j;
  dosWord push_pointer;

  // if we are waiting for the packet start
  if( l_slip_rx_status == netSRXS_WAITING_FOR_PACKET )
  {
    // check for packet start
    if( in_char == netSLIP_END )
    {
      // start packer reception
      l_slip_rx_status = netSRXS_RECEIVING_PACKET;
      l_rx_frame_size = 0;
      netCreateRXPacket(netPT_IP);
    }
    else
    {
			// only store received character when no transmit pending
			if( l_modem_tx_length == 0 )
			{
				// not SLIP package -> look for modem command
				l_modem_command_buffer[l_modem_rx_pointer++] = in_char;
				if( l_modem_rx_pointer>= netMODEM_COMMAND_BUFFER_LENGTH )
					l_modem_rx_pointer= 0;

				l_modem_command_buffer[l_modem_rx_pointer] = '\0';

				// calculate command length
				request_length = 0;
				while( l_connect_request[request_length] != '\0' )
					request_length++;

				// compare buffer with command (backward)
				i = request_length;
				j = l_modem_rx_pointer;
				while( l_modem_command_buffer[j] == l_connect_request[i] )
				{
					// check if request found
					if( i == 0 )
					{
						// request found, send response
						i = 0;
						push_pointer = 0;

						// copy response into the TX buffer
						while( l_connect_response[i] != '\0' && push_pointer < netMODEM_COMMAND_BUFFER_LENGTH )
						{
							// store character into the buffer
							l_modem_command_buffer[push_pointer++] = l_connect_response[i++];
						}

						// if succesfully copied start transmission
						if( l_connect_response[i] == '\0' )
						{
							l_modem_tx_pointer = 0;
							l_modem_tx_length = push_pointer;
							drvSLIPUARTEnableTransmitInterrupt();
						}
						
						break;
					}
					else
						i--;

					// update modem buffer index
					if( j == 0 )
						j = netMODEM_COMMAND_BUFFER_LENGTH - 1;
					else
						j--;
				}
			}
    }
  }
  else
  {
    // Check if the previous character was control character
    if( l_slip_rx_status == netSRXS_ESC_RECEIVED )
    {
      // if so decode it
      switch( in_char )
      {
        // store special character
        case netESC_END:
          if( netWriteByteRXPacket( netSLIP_END ) )
          {
            l_rx_frame_size++;
          }
          else
          {
            // if falied chancel packet
            netCancelRXPacket();
            l_slip_rx_status = netSRXS_WAITING_FOR_PACKET;
          }
          break;

        // store special character
        case netESC_ESC:
          if( netWriteByteRXPacket( netSLIP_ESC ) )
          {
            l_rx_frame_size++;
          }
          else
          {
            // if falied chancel packet
            netCancelRXPacket();
            l_slip_rx_status = netSRXS_WAITING_FOR_PACKET;
          }
          break;

        // SLIP Protocol violation
        default:
          break;
      }

      // Clear the special control character flag
      l_slip_rx_status = netSRXS_RECEIVING_PACKET;
    }
    else
    {
      switch( in_char )
      {
        // Special ESC Character received
        case netSLIP_ESC:
          l_slip_rx_status = netSRXS_ESC_RECEIVED;
          break;

        // Special END Character received
        case netSLIP_END:
          // Avoid zero length packets (0xC0-0xC0 conditions)
          if( l_rx_frame_size > 0 )
          {
            // store packet
            netStoreRXPacket();
          }
          else
          {
            // clear empty packet
            netCancelRXPacket();
          }
          
          // reopen a new packet
		      l_slip_rx_status = netSRXS_RECEIVING_PACKET;
					l_rx_frame_size = 0;
					netCreateRXPacket(netPT_IP);          
          break;

        // Data of Packet received
        default:
          if( netWriteByteRXPacket( in_char ) )
          {
            l_rx_frame_size++;

            // Avoid & discard large SLIP packets
            if( l_rx_frame_size > netSLIP_MAX_SIZE )
            {
              netCancelRXPacket();
              l_slip_rx_status = netSRXS_WAITING_FOR_PACKET;
            }
          }
          else
          {
            // if falied chancel packet
            netCancelRXPacket();
            l_slip_rx_status = netSRXS_WAITING_FOR_PACKET;
          }
          break;
      }
    }
  }
}
